#include "tfrmstruct.h"
#include "ui_tfrmstruct.h"
#include "tlr.h"
#include "tmonitortreedelegate.h"
#include "tlrsyntaxhighlighter.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QStyleFactory>
#include <QHeaderView>
#include <QToolTip>
#include <QSplitter>
#include <QFileDialog>
#include <QMessageBox>

TFrmStruct::TFrmStruct(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TFrmStruct)
{
    //UI初始化
    {
        ui->setupUi(this);

        m_treeStructMonitor->setItemDelegate(new TMonitorTreeDelegate(this));
        //设置文本框
        m_teStructFormat = new QTextEdit(this);
        QFont nfont;
        nfont.setFamily("Courier");
        nfont.setFixedPitch(true);
        nfont.setPointSize(10);
        m_teStructFormat->setFont(nfont);
        m_teStructFormat->setReadOnly(true);
        TLRSyntaxHighlighter *psyntaxhighlighter = new TLRSyntaxHighlighter(m_teStructFormat->document());
        psyntaxhighlighter->setSyntaxFormat(TSyntaxFormat::syntaxFmtCpp);
        m_teStructFormat->setVisible(false);
        //组合查看器
        QList<int> nsizes;
        nsizes.append(160);
        nsizes.append(200);
        QSplitter *pmonitordragableboundary = new QSplitter(this);
        pmonitordragableboundary->addWidget(m_teStructFormat);
        pmonitordragableboundary->addWidget(m_treeStructMonitor);
        pmonitordragableboundary->setStretchFactor(0,0);
        pmonitordragableboundary->setStretchFactor(1,1);
        pmonitordragableboundary->setSizes(nsizes);
        pmonitordragableboundary->setContentsMargins(0,0,0,0);
        //组合主面板
        QVBoxLayout *pvblcontent = new QVBoxLayout;
        pvblcontent->addWidget(pmonitordragableboundary);
        setLayout(pvblcontent);
        //结构体解析树属性
        QStringList nheaderlabels = {tr("名称"),tr("值"),"偏移","数据类型"};
        m_treeStructMonitor->setHeaderLabels(nheaderlabels);
        m_treeStructMonitor->header()->setSectionResizeMode(0,QHeaderView::Stretch);
        m_treeStructMonitor->setColumnWidth(1,160);
        m_treeStructMonitor->setColumnWidth(2,80);
        m_treeStructMonitor->setColumnWidth(3,80);
        m_treeStructMonitor->header()->setStretchLastSection(false);
        m_treeStructMonitor->setAlternatingRowColors(true);
        m_treeStructMonitor->setSelectionBehavior(QTreeWidget::SelectRows);
        m_treeStructMonitor->setSelectionMode(QTreeWidget::SingleSelection);
    }
    //信号槽
    {
        //结构体数据更新
        connect(TLR::get(),&TLR::sigStructDataUpdate,this,&TFrmStruct::slotStructDataUpdate);
        //树条目内容变化
        connect(m_treeStructMonitor,&QTreeWidget::itemChanged,this,&TFrmStruct::slotTreeWidgetItemChanged);
        //当前选中条目变更
        connect(m_treeStructMonitor,&QTreeWidget::currentItemChanged,this,&TFrmStruct::slotCurrentItemChanged);
    }
}

TFrmStruct::~TFrmStruct()
{
    delete ui;
}

/*
 * 重置
 * nindex : 结构体序号
 */
void TFrmStruct::reset(int nindex)
{
    m_index = nindex;
    dataToUI(TUIType::uiFrame);
}

/**
 * @brief 获取序号
 * @return 序号
 */
int TFrmStruct::getIndex()
{
    return m_index;
}

/*
 * 读取数据
 * nequidstr   : 设备识别字
 * nparastaadd : 设备起始地址
 * nip         : IP地址
 * nport       : 端口
 */
void TFrmStruct::rdPara(const QString &nequidstr,int nparastaadd,const QHostAddress &nip,int nport,int nserialaddr)
{
    TLRStruct nlrstruct = TLR::get()->m_lrStructModel.getStruct(m_index);
    auto nsndbytes = TLR::rdPara(nequidstr,nparastaadd,nlrstruct.getSize(),nserialaddr);
    TLR::get()->udpSockAnyWrite(nsndbytes,nip,nport);
}

/*
 * 读取数据(串口)
 * nequidstr : 设备识别字
 * nparastaadd : 数据起始地址
 * nserialaddr : 串口地址
 */
void TFrmStruct::rdParaSerial(const QString &nequidstr, int nparastaadd, int nserialaddr)
{
    TLRStruct nlrstruct = TLR::get()->m_lrStructModel.getStruct(m_index);
    auto nsndbytes = TLR::rdPara(nequidstr,nparastaadd,nlrstruct.getSize(),quint16(nserialaddr));
    TLR::get()->serialPortWrite(nsndbytes.front(),nlrstruct.getSize());
}

/**
 * @brief 读取数据(网络透传)
 * @param equid_str 设备识别字
 * @param para_sta_add 起始地址
 * @param ip IP
 * @param port 端口
 * @param serial_addr 串口地址
 * @param serial_port 串口号
 * @param baud 波特率
 */
void TFrmStruct::rdParaNetToSerial(const QString &equid_str, int para_sta_add, const QHostAddress &ip, int port, int serial_addr, int serial_port, int baud)
{
    TLRStruct lrstruct = TLR::get()->m_lrStructModel.getStruct(m_index);
    auto raw_data_list = TLR::rdPara(equid_str,para_sta_add,lrstruct.getSize(),quint16(serial_addr));
    auto send_data_list = TLR::packParaUartTRDat(serial_port,baud,raw_data_list,'T');
    TLR::get()->udpSockAnyWrite(send_data_list,ip,port);
}

/*
 * 下发数据
 * nequidstr   : 设备识别字
 * nparastaadd : 设备起始地址
 * nip         : IP地址
 * nport       : 端口
 */
void TFrmStruct::wrPara(const QString &nequidstr, int nparastaadd, const QHostAddress &nip, int nport,int nserialaddr)
{
    TLRStruct nlrstruct = TLR::get()->m_lrStructModel.getStruct(m_index);
    encodeTreeToStruct(nlrstruct);
    auto nsndbytes = TLR::wrPara(nequidstr,nparastaadd,nlrstruct.getSize(),nlrstruct.memData.constData(),nserialaddr);
    TLR::get()->udpSockAnyWrite(nsndbytes,nip,nport);
}

/**
 * @brief 下发数据(串口)
 * @param nequidstr
 * @param nparastaadd
 * @param nserialaddr
 */
void TFrmStruct::wrParaSerial(const QString &nequidstr, int nparastaadd, int nserialaddr)
{
    TLRStruct nlrstruct = TLR::get()->m_lrStructModel.getStruct(m_index);
    encodeTreeToStruct(nlrstruct);
    auto nsndbytes = TLR::wrPara(nequidstr,nparastaadd,nlrstruct.getSize(),nlrstruct.memData.constData(),nserialaddr);
    QList<int> readSizeList;
    for(int i=0;i<nsndbytes.size();i++) {
        readSizeList.append(90);
    }
    TLR::get()->serialPortWrite(nsndbytes,readSizeList);
}

/*
 * 控制数据
 * nequidstr   : 设备识别字
 * nparastaadd : 设备起始地址
 * nip         : IP地址
 * nport       : 端口
 */
void TFrmStruct::ctlPara(const QString &nequidstr, int nparastaadd, const QHostAddress &nip, int nport,int nserialaddr)
{
    TLRStruct nlrstruct = TLR::get()->m_lrStructModel.getStruct(m_index);
    encodeTreeToStruct(nlrstruct);
    auto nsndbytes = TLR::ctlPara(nequidstr,nparastaadd,nlrstruct.getSize(),nlrstruct.memData.constData(),nserialaddr);
    TLR::get()->udpSockAnyWrite(nsndbytes,nip,nport);
}

/**
 * @brief 控制数据(串口)
 * @param nequidstr
 * @param nparastaadd
 * @param nserialaddr
 */
void TFrmStruct::ctlParaSerial(const QString &nequidstr, int nparastaadd, int nserialaddr)
{
    TLRStruct nlrstruct = TLR::get()->m_lrStructModel.getStruct(m_index);
    encodeTreeToStruct(nlrstruct);
    auto nsndbytes = TLR::ctlPara(nequidstr,nparastaadd,nlrstruct.getSize(),nlrstruct.memData.constData(),nserialaddr);
    QList<int> readSizeList;
    for(int i=0;i<nsndbytes.size();i++) {
        readSizeList.append(90);
    }
    TLR::get()->serialPortWrite(nsndbytes,readSizeList);
}

/**
 * @brief 控制数据(单条)
 * @param equid_str   : 设备识别字
 * @param parasta_add : 起始地址
 * @param ip          : IP
 * @param port        : 端口
 * @param serial_add
 * @param error_str
 */
bool TFrmStruct::ctlPara_Single(const QString &equid_str,int parasta_add,const QHostAddress &ip,int port,int serial_add,QString &error_str)
{
    if(!m_cur_item) {
        error_str = "没有选中条目";
        return false;
    }
    if(m_cur_item->childCount() > 0) {
        error_str = "非基本数据类型无法下载";
        return false;
    }
    int offset = m_cur_item->text(2).toInt() + parasta_add;
    bool convert_ok = false;
    bool signed_type = false;
    qint64 val_raw = 0;
    qint64 val_raw_s64;
    quint64 val_raw_u64;
    QString val_str = m_cur_item->text(1);
    QString type_str = m_cur_item->text(3);
    QByteArray send_data;

    //判断是否有符号数据
    if((type_str == "s16") || (type_str == "s32") || (type_str == "char") || (type_str == "s64")) {
        signed_type = true;
    }
    else {
        signed_type = false;
    }
    //u64 s64需要和普通数据格式区分开来，因为无法使用u64或者s64来互相表示
    if((type_str == "u64") || (type_str == "s64")) {
        if(val_str.startsWith("0x")) {
            if(signed_type) {
                val_raw_s64 = val_str.toLongLong(&convert_ok,16);
            }
            else {
                val_raw_u64 = val_str.toULongLong(&convert_ok,16);
            }
        }
        else {
            if(signed_type) {
                val_raw_s64 = val_str.toLongLong(&convert_ok,10);
            }
            else {
                val_raw_u64 = val_str.toULongLong(&convert_ok,10);
            }
        }
    }
    //其它数据类型
    else {
        if(val_str.startsWith("0x")) {
            if(signed_type) {
                val_raw = val_str.toInt(&convert_ok,16);
            }
            else {
                val_raw = val_str.toUInt(&convert_ok,16);
            }
        }
        else {
            if(signed_type) {
                val_raw = val_str.toInt(&convert_ok,10);
            }
            else {
                val_raw = val_str.toUInt(&convert_ok,10);
            }
        }
    }
    if(!convert_ok) {
        error_str = "数据填写错误";
        return false;
    }
    if(type_str == "char") {
        char val_char = val_raw;
        send_data = TLR::ctlPara(equid_str,offset,1,&val_char,serial_add).at(0);
    }
    else if(type_str == "u8") {
        u8 val_u8 = val_raw;
        send_data = TLR::ctlPara(equid_str,offset,1,&val_u8,serial_add).at(0);
    }
    else if(type_str == "u16") {
        u16 val_u16 = val_raw;
        send_data = TLR::ctlPara(equid_str,offset,2,&val_u16,serial_add).at(0);
    }
    else if(type_str == "s16") {
        s16 val_s16 = val_raw;
        send_data = TLR::ctlPara(equid_str,offset,2,&val_s16,serial_add).at(0);
    }
    else if(type_str == "u32") {
        u32 val_u32 = val_raw;
        send_data = TLR::ctlPara(equid_str,offset,4,&val_u32,serial_add).at(0);
    }
    else if(type_str == "s32") {
        s32 val_s32 = val_raw;
        send_data = TLR::ctlPara(equid_str,offset,4,&val_s32,serial_add).at(0);
    }
    else if(type_str == "u64") {
        send_data = TLR::ctlPara(equid_str,offset,8,&val_raw_u64,serial_add).at(0);
    }
    else if(type_str == "s64") {
        send_data = TLR::ctlPara(equid_str,offset,8,&val_raw_s64,serial_add).at(0);
    }
    TLR::get()->udpSockAnyWrite(send_data,ip,port);
    error_str = "没有错误";

    return true;
}

/**
 * @brief 导入数据
 */
void TFrmStruct::importData()
{
    TLRStruct &lrStruct = TLR::get()->m_lrStructModel.m_lrstructModel[m_index];
    QString filePath = QFileDialog::getOpenFileName(this,tr("打开数据文件"),"","Data File (*.bin)");
    if(filePath.isEmpty()) {
        return;
    }
    QFile dataFile(filePath);
    dataFile.open(QFile::ReadOnly);
    QByteArray data = dataFile.readAll();
    dataFile.close();
    if(data.size() == lrStruct.getSize()) {
        lrStruct.memParaCopy(data.constData(),0,data.size());
        slotStructDataUpdate(m_index);
        QMessageBox::information(this,tr("提示"),tr("导入成功"));
    }
    else {
        QMessageBox::warning(this,tr("提示"),tr("数据长度错误"));
    }
}

/**
 * @brief 导出数据
 */
void TFrmStruct::exportData()
{
    TLRStruct lrStruct = TLR::get()->m_lrStructModel.getStruct(m_index);
    QString filePath = QFileDialog::getSaveFileName(this,tr("保存数据文件"),lrStruct.name + "_data.bin","Data File (*.bin)");
    if(filePath.isEmpty()) {
        return;
    }
    encodeTreeToStruct(lrStruct);
    QFile dataFile(filePath);
    dataFile.open(QFile::WriteOnly);
    dataFile.write(lrStruct.memData);
    dataFile.close();
    QMessageBox::information(this,tr("提示"),tr("导出成功"));
}

/*
 * UI操作
 * nuitype : UI操作类型
 */
void TFrmStruct::dataToUI(const TUIType &nuitype)
{
    switch (nuitype) {
    case TUIType::uiFrame: {
        auto &nlrstructmodel = TLR::get()->m_lrStructModel;
        TLRStruct nlrstruct = nlrstructmodel.getStruct(m_index);
        //结构体原始格式
        m_teStructFormat->setPlainText(nlrstruct.toCppString());
        //结构体分析
        m_treeStructMonitor->clear();
        decodeStructToTree(0,nlrstruct,nlrstruct.memData);
        break;
    }
    }
}

/*
 * 结构体解析到树
 * npreoffset  : 偏移
 * nlrstruct   : 待解析的结构体
 * nactmemdata : 实际的内存数据
 * prootitem   : 上一级节点
 */
void TFrmStruct::decodeStructToTree(int npreoffset,const TLRStruct &nlrstruct,const QByteArray nactmemdata,QTreeWidgetItem *prootitem)
{
    for(int istructmember=0;istructmember<nlrstruct.member.size();istructmember++) {
        const TLRStructMember &nlrstructmember = nlrstruct.member.at(istructmember);
        QTreeWidgetItem *ptopitem = new QTreeWidgetItem;
        //是否可编辑
        if(nlrstructmember.isEditable()) {
            Qt::ItemFlags ntopitemflags = ptopitem->flags();
            ptopitem->setFlags(ntopitemflags | Qt::ItemIsEditable);
        }
        //名称
        ptopitem->setText(0,nlrstructmember.name);
        //值
        ptopitem->setText(1,nlrstructmember.getValStr(nactmemdata,npreoffset));
        ptopitem->setTextColor(1,nlrstructmember.getValColor());
        if(!nlrstructmember.hint.isEmpty()) {
            ptopitem->setToolTip(1,nlrstructmember.hint);
        }
        //偏移
        ptopitem->setText(2,nlrstructmember.getOffsetStr(npreoffset));
        ptopitem->setTextColor(2,QColor(160,160,160));
        //数据类型
        ptopitem->setText(3,nlrstructmember.getFullType());
        ptopitem->setTextColor(3,QColor(160,160,160));
        //处理数组类型
        if(nlrstructmember.isArray()) {
            for(int iarr=0;iarr<nlrstructmember.count;iarr++) {
                QTreeWidgetItem *psubitem = new QTreeWidgetItem;
                if(nlrstructmember.isEditable(iarr)) {
                    Qt::ItemFlags nsubitemflags = psubitem->flags();
                    psubitem->setFlags(nsubitemflags | Qt::ItemIsEditable);
                }
                //名称
                psubitem->setText(0,QString("[%1]").arg(iarr));
                //值
                psubitem->setText(1,nlrstructmember.getValStr(nactmemdata,npreoffset,iarr));
                psubitem->setTextColor(1,nlrstructmember.getValColor(iarr));
                //偏移
                psubitem->setText(2,nlrstructmember.getOffsetStr(npreoffset,iarr));
                psubitem->setTextColor(2,QColor(160,160,160));
                //数据类型
                psubitem->setText(3,nlrstructmember.type);
                psubitem->setTextColor(3,QColor(160,160,160));
                //处理自定义数据类型
                if(!nlrstructmember.isPlainDataType()) {
                    TLRStruct nlrstructtodecode = TLR::get()->m_lrStructModel.getStruct(nlrstructmember.type);
                    decodeStructToTree(nlrstructmember.getOffset(npreoffset,iarr),nlrstructtodecode,nactmemdata,psubitem);
                }
                ptopitem->addChild(psubitem);
            }
        }
        //非数组类型
        else {
            //处理自定义数据类型
            if(!nlrstructmember.isPlainDataType()) {
                TLRStruct nlrstructtodecode = TLR::get()->m_lrStructModel.getStruct(nlrstructmember.type);
                decodeStructToTree(nlrstructmember.getOffset(npreoffset),nlrstructtodecode,nactmemdata,ptopitem);
            }
        }
        if(prootitem) {
            prootitem->addChild(ptopitem);
        }
        else {
            m_treeStructMonitor->addTopLevelItem(ptopitem);
        }
    }
}

/*
 * 树恢复到结构体
 * nlrstruct : 结构体数据
 */
void TFrmStruct::encodeTreeToStruct(TLRStruct &nlrstruct)
{
    for(int itopitem=0;itopitem<m_treeStructMonitor->topLevelItemCount();itopitem++) {
        QTreeWidgetItem *ptopitem = m_treeStructMonitor->topLevelItem(itopitem);
        if(ptopitem) {
            queryTreeItemToStruct(ptopitem,nlrstruct);
        }
    }
}

/*
 * 遍历树条目到结构体
 * ptreeitem : 树条目
 * nlrstruct : 结构体数据
 */
void TFrmStruct::queryTreeItemToStruct(const QTreeWidgetItem *ptreeitem,TLRStruct &nlrstruct)
{
    if(ptreeitem) {
        if(ptreeitem->childCount() == 0) {
            nlrstruct.setPara(ptreeitem->text(2),ptreeitem->text(3),ptreeitem->text(1));
        }
        else {
            for(int i=0;i<ptreeitem->childCount();i++) {
                QTreeWidgetItem *psubtreeitem = ptreeitem->child(i);
                queryTreeItemToStruct(psubtreeitem,nlrstruct);
            }
        }
    }
}

/*
 * 遍历结构体到树条目
 * ptreeitem : 树条目
 * nlrstruct : 结构体
 */
void TFrmStruct::queryStructToTreeItem(QTreeWidgetItem *ptreeitem,const TLRStruct &nlrstruct)
{
    if(ptreeitem) {
        if(ptreeitem->childCount() == 0) {
            QString nvalstr = nlrstruct.getPara(ptreeitem->text(2),ptreeitem->text(3));
            ptreeitem->setText(1,nvalstr);
        }
        else {
            if(ptreeitem->text(3).startsWith("char[")) {
                QString valStr = nlrstruct.getPara(ptreeitem->text(2),ptreeitem->text(3),ptreeitem->childCount());
                ptreeitem->setText(1,valStr);
            }
            for(int i=0;i<ptreeitem->childCount();i++) {
                QTreeWidgetItem *psubtreeitem = ptreeitem->child(i);
                queryStructToTreeItem(psubtreeitem,nlrstruct);
            }
        }
    }
}

/*
 * 结构体数据更新
 * index : 序号
 */
void TFrmStruct::slotStructDataUpdate(int index)
{
    if(index != m_index) {
        return;
    }
    TLRStruct nlrstruct = TLR::get()->m_lrStructModel.getStruct(m_index);
    for(int itopitem=0;itopitem<m_treeStructMonitor->topLevelItemCount();itopitem++) {
        QTreeWidgetItem *ptopitem = m_treeStructMonitor->topLevelItem(itopitem);
        //特殊处理字符串
        QString ntypestr = ptopitem->text(3);
        if(ntypestr.startsWith("char[") && (ptopitem->childCount() > 0)) {
            QString nvalstr = nlrstruct.getPara(ptopitem->text(2),ptopitem->text(3),ptopitem->childCount());
            ptopitem->setText(1,nvalstr);
        }
        if(ptopitem) {
            queryStructToTreeItem(ptopitem,nlrstruct);
        }
    }
}

/*
 * 分析树条目发生变化
 */
void TFrmStruct::slotTreeWidgetItemChanged(QTreeWidgetItem *item, int column)
{
    Q_UNUSED(column)
    //处理字符串
    if(item->childCount() > 0) {
        QByteArray str_to_edit = item->text(1).toLocal8Bit();
        for(int i=0;i<item->childCount();i++) {
            QTreeWidgetItem *psubitem = item->child(i);
            if(psubitem) {
                if(psubitem->text(3) == "char") {
                    if(i<str_to_edit.size()) {
                        char text_unit = str_to_edit.at(i);
                        psubitem->setText(1,QString("0x%1").arg((quint8)text_unit,2,16,QChar('0')));
                    }
                    else {
                        psubitem->setText(1,"0x00");
                    }
                }
            }
        }
    }
}

/**
 * @brief 项目激活
 * @param item
 * @param column
 */
void TFrmStruct::slotCurrentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
    Q_UNUSED(previous)
    m_cur_item = current;
}
